﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq;
using System.Web.Configuration;
using planerka.DataAccess.Models;

namespace planerka.DataAccess.DataService
{
    // http://www.codeproject.com/Articles/26667/Implementing-the-Repository-Pattern-with-LINQ-to-S
    // http://www.remondo.net/repository-pattern-example-csharp/ - The Repository Pattern Example in C#
    // http://www.c-sharpcorner.com/uploadfile/scottlysle/generic-data-access-using-linq-to-sql-and-C-Sharp/
    // http://forums.asp.net/t/1629771.aspx/1 - The Repository Pattern on LINQ
    // http://omaralzabir.com/ - Omar
    // http://www.gotdotnet.ru/blogs/csharpsoft/12447/ - Беспроблемный шаблон Repository

    public interface IRepository<T> where T : class
    {
        /// <summary>
        /// Return all instances of type T.
        /// </summary>
        /// <returns></returns>
        IEnumerable<T> All();

        /// <summary>
        /// Return all instances of type T that match the expression exp.
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        IEnumerable<T> FindAll(Func<T, bool> exp);

        /// <summary>Returns the single entity matching the expression.
        /// Throws an exception if there is not exactly one such entity.</summary>
        /// <param name="exp"></param><returns></returns>
        T Single(Func<T, bool> exp);

        /// <summary>Returns the first element satisfying the condition.</summary>
        /// <param name="exp"></param><returns></returns>
        T First(Func<T, bool> exp);

        /// <summary>
        /// Mark an entity to be deleted when the context is saved.
        /// </summary>
        /// <param name="entity"></param>
        void MarkForDeletion(T entity);
        void MarkAllForDeletion(IEnumerable<T> entities);

        /// <summary>
        /// Create a new instance of type T.
        /// </summary>
        /// <returns></returns>
        T InsertInstance(T entity);

        /// <summary>Persist the data context.</summary>
        void Save();
    }

    public interface IDataContextFactory
    {
        System.Data.Linq.DataContext Context { get; }
        void SaveAll();
    }

    //public void DataContextFactory()
    //{

    //}

    //public class _LinqRepository<T> : IRepository<T> where T : class
    public class _Repository<T> : IRepository<T>, IDisposable
    where T : class
{
    //protected IDataContextFactory _dataContextFactory;
        protected DataContext dataContext = new linq2sqlDataContext(WebConfigurationManager.ConnectionStrings["ApplicationServices"].ToString());
    /// <summary>
    /// Return all instances of type T.
    /// </summary>
    /// <returns></returns>
    public IEnumerable<T> All()
    {
        return GetTable;
    }

    /// <summary>
    /// Return all instances of type T that match the expression exp.
    /// </summary>
    /// <param name="exp"></param>
    /// <returns></returns>
    public IEnumerable<T> FindAll(Func<T, bool> exp)
    {
        return GetTable.Where<T>(exp);
    }

    /// <summary>See IRepository.</summary>
    /// <param name="exp"></param><returns></returns>
    public T Single(Func<T, bool> exp)
    {
        return GetTable.Single(exp);
    }

    // PRESERVE - HUGE PERFOMANCE!
    //protected T Single(System.Linq.Expressions.Expression<Func<T, bool>> exp)
    //{
    //    return GetTable.Single(exp);
    //}

    /// <summary>See IRepository.</summary>
    /// <param name="exp"></param><returns></returns>
    public T First(Func<T, bool> exp)
    {
        return GetTable.First(exp);
    }

    /// <summary>See IRepository.</summary>
    /// <param name="entity"></param>
    public virtual void MarkForDeletion(T entity)
    {
        dataContext.GetTable<T>().DeleteOnSubmit(entity);        
    }

    /// <summary>See IRepository.</summary>
    /// <param name="entity"></param>
    public virtual void MarkAllForDeletion(IEnumerable<T> entities)
    {
        dataContext.GetTable<T>().DeleteAllOnSubmit(entities);
    }

    /// <summary>
    /// Create a new instance of type T.
    /// </summary>
    /// <returns></returns>
    public virtual T InsertInstance(T entity)
    {
        //T entity = Activator.CreateInstance<T>();
        GetTable.InsertOnSubmit(entity);
        return entity;
    }

    /// <summary>See IRepository.</summary>
    public void Save()
    {
        //_dataContextFactory.SaveAll();
        dataContext.SubmitChanges();
    }

    public _Repository() //IDataContextFactory dataContextFactory)
    {
        dataContext = new linq2sqlDataContext(WebConfigurationManager.ConnectionStrings["ApplicationServices"].ToString());
    }

    public void Dispose()
    {
        if (dataContext != null)
        {
            dataContext.Dispose();
        }
    }

    
    #region Properties

    private string PrimaryKeyName
    {
        get { return TableMetadata.RowType.IdentityMembers[0].Name; }
    }

    private System.Data.Linq.Table<T> GetTable
    {
        get { 
            //return _dataContextFactory.Context.GetTable<T>();
            return dataContext.GetTable<T>(); 
        }
    }

    private System.Data.Linq.Mapping.MetaTable TableMetadata
    {
        get { 
            //return _dataContextFactory.Context.Mapping.GetTable(typeof(T));
            return dataContext.Mapping.GetTable(typeof(T)); 
        }
    }

    private System.Data.Linq.Mapping.MetaType ClassMetadata
    {
        get { 
            //return _dataContextFactory.Context.Mapping.GetMetaType(typeof(T));
            return dataContext.Mapping.GetMetaType(typeof(T)); 
        }
    }

    #endregion
}
}